﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace GTAHookAdv
{
    /// <summary>
    /// This helper add sound functionnality, you can play 2D or 3D sounds from any audio file or audio stream.
    /// </summary>
    public static class Sound
    {
      
        static IrrKlang.ISoundEngine _engine = InitEngine();
        static Dictionary<int, IrrKlang.ISoundSource> _soundsCach = new Dictionary<int, IrrKlang.ISoundSource>();
        private static IrrKlang.ISound _frontendSound;
        private static List<IrrKlang.ISound> _frontendPersistents = new List<IrrKlang.ISound>();


        private static void ClearPersistentSounds()
        {
            List<IrrKlang.ISound> todel = new List<IrrKlang.ISound>();
            foreach (IrrKlang.ISound item in _frontendPersistents)
            {
                if (item.Finished)
                {
                    try
                    {
                        item.Dispose();
                    }
                    catch { }
                    todel.Add(item);
                }
            }
            foreach (IrrKlang.ISound item in todel)
            {
                _frontendPersistents.Remove(item);
            }
        }
        private static IrrKlang.ISoundEngine InitEngine()
        {
            try
            {
                IrrKlang.ISoundEngine result = new IrrKlang.ISoundEngine(IrrKlang.SoundOutputDriver.AutoDetect, IrrKlang.SoundEngineOptionFlag.DefaultOptions);
                result.LoadPlugins(Environment.CurrentDirectory);
                return result;
            }
            catch
            {
                return null;
            }
        }

        private static IrrKlang.ISoundSource FilePathToSound(String filepath)
        {
            if (!File.Exists(filepath))
                throw new FileNotFoundException(filepath);
            int hc = filepath.GetHashCode();
            IrrKlang.ISoundSource source;
            if (!_soundsCach.TryGetValue(hc, out source))
            {
                source = _engine.AddSoundSourceFromFile(filepath, IrrKlang.StreamMode.AutoDetect, true);
                _soundsCach.Add(hc, source);
            }
            return source;
        }

        /// <summary>
        /// Load the sound file and store it into memory.
        /// </summary>
        /// <param name="filepath">partial or full path to the sound file (*.wav,*.mp3,*.ogg,*.flac)</param>
        /// <returns>true if operation succeed</returns>
        /// <exception cref="FileNotFoundException">guess :)</exception>
        /// <remarks>A preloaded sound consums memory, but will start player faster when you'll call a PlayXXX method.
        /// A Good practice is to preload sounds that needs to be triggered as fast as possible, and to use <see cref="ClearCache"/> when you want to free memory</remarks>
        /// <example> see <see cref="DisposeAll"/> for an example</example>
        public static bool PreloadSound(String filepath)
        {
            if (!File.Exists(filepath))
                throw new FileNotFoundException(filepath);
            if (_engine == null)
                return false;
            try
            {
                FilePathToSound(filepath);
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Free memory from all loaded sounds, but keep Sound engine loaded
        /// </summary>
        public static void ClearCache()
        {
            try
            {
                foreach (IrrKlang.ISoundSource item in _soundsCach.Values)
                {
                    item.Dispose();
                }
                _soundsCach.Clear();
            }
            catch { }
        }

        /// <summary>
        /// Play a .wav or .mp3 as frontend. File will be stored in cache for futur replay, use ClearCache if you need to free memory.
        /// </summary>
        /// <param name="filepath">full path (or partial from GTA root folder) to the sound file</param>
        ///<remarks> see <see cref="DisposeAll"/> for an example</remarks>
        public static void PlayFrontEnd(String filepath)
        {
            PlayFrontEnd(filepath, 100,false);
        }

        /// <summary>
        /// Play a .wav or .mp3 as frontend. File will be stored in cache for futur replay, use ClearCache if you need to free memory.
        /// </summary>
        /// <param name="filepath">full path (or partial from GTA root folder) to the sound file</param>
        /// <param name="volume">from 0 to 100</param>
        public static void PlayFrontEnd(String filepath, ushort volume)
        {
            PlayFrontEnd(filepath, volume, false);
        }

         /// <summary>
        /// Play a .wav or .mp3 as frontend. File will be stored in cache for futur replay, use ClearCache if you need to free memory.
        /// </summary>
        /// <param name="filepath">full path (or partial from GTA root folder) to the sound file</param>
        /// <param name="persistent">if true, the sound will not be interrupted by an other frontend sound played (it will play until the end) false by default</param>
        public static void PlayFrontEnd(String filepath, bool persistent)
        {
            PlayFrontEnd(filepath, 100, persistent);
        }
        
        /// <summary>
        /// Play a .wav or .mp3 as frontend. File will be stored in cache for futur replay, use ClearCache if you need to free memory.
        /// </summary>
        /// <param name="filepath">full path (or partial from GTA root folder) to the sound file</param>
        /// <param name="volume">from 0 to 100</param>
        /// <param name="persistent">if true, the sound will not be interrupted by an other frontend sound played (it will play until the end) false by default</param>
        public static void PlayFrontEnd(String filepath, ushort volume,bool persistent)
        {
            try
            {
                IrrKlang.ISoundSource source = FilePathToSound(filepath);
                source.DefaultVolume = volume;
                if (_frontendSound != null)
                {
                    try
                    {
                        _frontendSound.Stop();
                        
                    }
                    catch { }
                    try
                    {
                        _frontendSound.Dispose();
                    }
                    catch { }
                }
                if (persistent)
                    _frontendPersistents.Add(_engine.Play2D(source, false, false, true));
                else
                    _frontendSound = _engine.Play2D(source, false, false, true);
                
            }
            catch { }
            ClearPersistentSounds();
        }

        /// <summary>
        /// Invalidate all cached sounds, then unload the sound engine.
        /// You should call it when your script is unloaded to ensure a safe ressources cleaning.
        /// </summary>
        /// <example>
        /// this example shows how to preload sounds then clear properly used ressources.
        /// A sound will be played everytime the player press spacebar
        /// <code>public class MyScript : GTA.Script
        ///{
        ///    MyScript()
        ///    {
        ///        //This event is called when your script is unloaded, this is a good place to dispose ressources
        ///        AppDomain.CurrentDomain.DomainUnload += CurrentDomain_DomainUnload;
        ///        //We preload sound, so Play methods will excecute faster.
        ///        Sound.PreloadSound(@".Sounds\Mysound.mp3"); //File should be into c:\Program Files\GTA4\Sounds\ folder
        ///        
        ///        this.KeyDown += MyScript_KeyDown;
        ///    }
        ///
        ///    void MyScript_KeyDown(object sender, GTA.KeyEventArgs e)
        ///    {
        ///        if (e.Key == System.Windows.Forms.Keys.Space)
        ///            Sound.PlayFrontEnd(@".Sounds\Mysound.mp3"); //Will execute pretty fast as the sound been preloaded
        ///    }
        ///
        ///    void CurrentDomain_DomainUnload(object sender, EventArgs e)
        ///    {
        ///        Sound.DisposeAll(); //This ensure that all ressources (sound engine, memory used) will be destroyed properly.
        ///    }
        ///}</code></example>
        public static void DisposeAll()
        {
            ClearPersistentSounds();
            ClearCache();
            try
            {
                Sound._frontendSound.Dispose();
            }
            catch { }
            try
            {
                Sound._engine.Dispose();
            }
            catch { }
            _frontendSound = null;
            _frontendPersistents = null;
            _engine = null;
            GC.Collect();
        }
    }

    
}
